#!/run/current-system/profile/bin/guile \
-e main -s
!#

;; This is a program that checks to see if a string of numbers has
;; repeatitive numbers...
;;  take any number, if even / 2,  if odd * 3 + 1  repeat.
;; The goal is to plot the number of steps it takes to produce 1 from 1-100
;; gnuplot can plot things via a command line from standard in via
;; gnuplot -

;; To give gnuplot commands directly in the command line, using the "-persist" option so that the plot remains on the screen afterwards:
;; gnuplot -persist -e "set title 'Sine curve'; plot sin(x)"
;; To set user-defined variables a and s prior to executing commands from a file:
;; gnuplot -e "a=2; s='file.png'" input.gpl

;; use these modules to help my process the command line arguments.
(use-modules (ice-9 getopt-long)
             ;;(use-modules (charting))
             (oop goops)
             ;; (use-modules (fibers))
             (ice-9 textual-ports)) ;;open-output-file

;; take a number
;; if even / 2
;; if odd * 3 + 1
;; repeat
;; You should end at 1
;;(define counter 0)

;; Every time you call counter, it returns the next higher positive integer
;; (counter #:key #t) resets the value to 0
(define counter
  (let ((counter 0))
    (lambda* (#:key reset)
      (if reset
          (set! counter 0)
          (begin
            (set! counter (+ counter 1))
            counter)))))


;; implement the collatz (3 + 1) algorithm on a number.
;; Return the number of steps it takes to turn the number into 1
(define (collatz n)
  (when (>= n 1) ;; n must be greater than or equal to 1
    (let ((count (counter)))
      (cond
       ((= n 1)
        (counter #:reset #t)
        (- count 1))
       ((even? n)
        ;; (display (string-append "collatz (/ " (number->string n) " 2)\n"))
        (collatz (/ n 2)))
       ((odd? n)
        ;; (display (string-append  "collatz (- (* " (number->string n) " 3) 1)\n"))
        (collatz (+ (* n 3) 1)))))))

;; return the number with the most iterations required to get to 1
;; within the set 0-n.  return value is '(number number)
(define (collatz-set n)
  ;; most iterative number the number that takes the most iterations
  ;; to get to 1. (cdr most-it-number) is the number
  ;; (car (cdr most-it-number)) is the number of iterations
  (let ([most-it-number '(0 0)]
        [iterations 0])
    (let loop ((number n)) ;; loop through the numbers 0-n
      (when (> number 1)
        (set! iterations (collatz number))
        ;; error checking (display number) (display " has ")
        ;; error checking (display iterations) (display " iterations\n")
        ;; if the current number's iterations was more than the last one
        ;; then set most-it-number to the new number
        (when (> iterations
                 (car (cdr most-it-number)))
          (set! most-it-number (list number iterations)))
        (loop (- number 1))))
    most-it-number))

;; this function will create a gnuplot script to draw a line through a scatter plot
(define (create-plot-script data-file)
  ;; use a let here instead of all the defines
  (letrec ([script-filename "/tmp/plot.dem"]
           [output-script-port (open-output-file script-filename)])

    (put-string output-script-port "set style fill  transparent solid 1 noborder\n")
    (put-string output-script-port "set style circle radius 0.5\n")
    (put-string output-script-port
                (string-append "plot '" data-file "' with circles"))
    (force-output output-script-port)
    (close-port output-script-port)
    script-filename))



;; graphs a scatter plot from the set of numbers 2-n
;; what's wrong here?  and wow functional programming recursively sure makes for consice code!
(define (graph-collatz-set n)
  ;; number-iterations is the list of numbers and their iterations
  ;; ie:  '((2 . 1) (3 . 8) (4 . 2) ...)
  (letrec ([data-filename "/tmp/data.dat"]
           [output-data-port (open-output-file data-filename)]
           [script-filename (create-plot-script data-filename)])
    (let loop ((number 2) (iterations (collatz 2))) ;; loop through the numbers 0-n
      (when (>= n number)
        ;; write to a file
        (put-string output-data-port
                    (string-append (number->string number) "\t" (number->string iterations) "\n"))
        ;;(display (string-append "number is " number " iterations is " iterations "\n"))
        (loop (+ number 1) (collatz (+ number 1)))))
    (force-output output-data-port)
    (close-port output-data-port)
    (system (string-append "gnuplot "  "-p " script-filename)))
  ;;(delete-file data-filename)
  ;;(delete-file script-filename)
  ;; if guile-charting worked
  ;; (make-scatter-plot
  ;;  "3+1 conjecture"
  ;;  (list
  ;;   (append '("numbers")
  ;;           (let loop ((number 2) (iterations (collatz 2))) ;; loop through the numbers 0-n
  ;;             (if (>= n number)
  ;;                 (cons (cons number iterations) (loop (+ number 1) (collatz (+ number 1))))
  ;;                 '())))))
  )


(define (main args)
  ;;the option specification tells getopt-long how
  ;; to parse the command line
  (let* ((option-spec '((version (single-char #\v) (value #f))
                        (help    (single-char #\h) (value #f))
                        (graph   (single-char #\g) (value #t))
                        (number  (single-char #\n) (value #t))
                        ;; a set of numbers
                        (set     (single-char #\s) (value #t))
                        ))
         ;; tell getopt-long to parse the command line and put the
         ;; data in options
         (options (getopt-long args option-spec))
         ;; was  --help or -h used?
         (help-wanted    (option-ref options 'help #f))
         (version-wanted (option-ref options 'version #f))
         ;; fixme.  This How do I know if graph was wanted?
         (graph-wanted   (option-ref options 'graph #f))
         (number-wanted  (option-ref options 'number #f))
         (set-wanted     (option-ref options 'set #f)))
    (if (or number-wanted version-wanted help-wanted set-wanted graph-wanted)
        (cond
         (version-wanted
          (display "collatz version 0.1\n"))
         (help-wanted
          (display "collatz [options]\n")
          (display "")
          (display "-v  --version  Display version\n")
          (display "-h, --help     Display this help info\n")
          (display "-g, --graph    Graph the set of numbers' iterations via gnuplot\n")
          (display "               ie: collatz -g 500\n")
          (display "-n, --number   Returns the number of iterations required to turn this number to 1\n")
          (display "-s, --set      Specify a set of numbers, to which to apply the 3 + 1 formula.\n")
          (display "               ie: collatz -s 500 will return the number which requires the most\n")
          (display "               iterations in the set of numbers 2-500\n")
          )
         (number-wanted
          (display (collatz (string->number number-wanted)))
          (display "\n"))
         (set-wanted
          (let ((result (collatz-set (string->number set-wanted))))
            (display "In the set of 2-")
            (display set-wanted)
            (display " ")
            (display (car result))
            (display " has the most iterations with ")
            (display (car (cdr result)))
            (display " iterations\n")))
         (graph-wanted
          (graph-collatz-set (string->number graph-wanted)))))))
